#include "encode.h"
#include "configs.h"
#include <vector>
#include "channel.h"

namespace BCHEncoder{

  // compute the power of a s.t. a^k is a conjugate of a
  void compute_conjugates(std::vector<int>& C, const long& i){
    GF2E a = to_GF2E(GF2X(1, 1));
    GF2E an = power(a, i);
    long k = 1;
    C.push_back(i);
    GF2E conjugate = power(an, 2);
    while ( an != conjugate ) {
      C.push_back( (i * ((long) pow((double) 2, (double) k))) % ((long) pow((double) 2, (double) BCHm) - 1));
      ++k;
      conjugate = power(an, (long) pow((double) 2, (double) k));
    }
  }

  // compute the minimal polynomial of a^i
  void compute_minimal_polynomial(GF2X& minpol, const long& i){
    GF2X ai(i, 1);
    GF2XModulus p(BCHpol);
    rem(ai, ai, p);
    MinPolyMod(minpol, ai, p);
  }


  // compute the generator polynomial of the BCH code given the specifications
  void compute_generator_polynomial(GF2X& genpol){    
    genpol = to_GF2X(1);
    GF2X minpol;
    for(long i = 1; i <= (2 * BCHt - 1); i+=2){
      compute_minimal_polynomial(minpol, i);
      genpol *= minpol;
    }
  }

  // convert the message bits to its polynomial representation
  // where the i-th message bit is the coefficient of the term x^(i-1)
  void vec_to_poly(GF2X& pol, const vec_GF2& msg){
    pol = to_GF2X(0);
    GF2X term;
    for(long i = 1; i <= msg.length(); ++i) {
      term = GF2X(i - 1, msg(i));
      pol += term;
    }
  }

  // convert the polynomial representation of a codeword back to 
  // its vector representation by extracting the polynomial coeffs.
  void poly_to_vec(vec_GF2& v, const GF2X& p){
    v.SetLength(BCHn);
    long d = deg(p);
    for(long i = 0; i <= d; ++i){
      v(i+1) = coeff(p, i);
    }
    // the entries which are not touched are by default to be 0.
  }

  // encode the message with BCH code, store in codeword
  void encode(vec_GF2& codeword, const vec_GF2& msg, const GF2X& genpol) {
    GF2X msg_pol;
    GF2X codeword_pol;
    vec_to_poly(msg_pol, msg);
    codeword_pol = msg_pol * genpol;
    poly_to_vec(codeword, codeword_pol);
  }
  
  // generate a vector of random message bits.
  // the length of message bits = BCHn - deg(p)
  void generate_random_message(vec_GF2& msg, const GF2X& genpol){
    srand(time(NULL) * rand());
    sleep(0.2);
    SetSeed( to_ZZ(rand()) );
    BCHk = BCHn - deg(genpol);
    msg = random_vec_GF2(BCHk);
  }
  
  // generate a random codeword                                                                                       
  void generate_random_codeword(vec_GF2& codeword, const GF2X& genpol){
    codeword.SetLength(BCHn);
    vec_GF2 msg;
    generate_random_message(msg, genpol);
    encode(codeword, msg, genpol);
  }


  void generate_random_noisy_codeword(vec_GF2& noisy_codeword, 
				      vec_GF2& pure_codeword, 
				      const GF2X& genpol){
    noisy_codeword.kill();
    pure_codeword.kill();
    vec_GF2 msg;
    pure_codeword.SetLength(BCHn);
    generate_random_message(msg, genpol);
    encode(pure_codeword, msg, genpol);
    noisy_codeword = pure_codeword;
    BCHChannel::add_noise_bsc(noisy_codeword);

#ifdef DEBUG
    std::cout << "Message bits: " << msg << std::endl;
    std::cout << "Pure codeword: " << pure_codeword << std::endl;
    std::cout << "Noisy codeword: " << noisy_codeword << std::endl;
#endif
  }


}
